﻿
Public Module _compare
    Public Function isComparable(Of T)(ByVal this As T) As Boolean
        Return istype(Of IComparable, T)(this)
    End Function

    Public Function isComparable(Of T, CompareTo)(ByVal this As T) As Boolean
        Return istype(Of IComparable(Of CompareTo), T)(this)
    End Function

    Public Function isComparable(Of T)() As Boolean
        Dim this As T = Nothing
        this = alloc(Of T)()
        Return isComparable(this)
    End Function

    Public Function isComparable(Of T, CompareTo)() As Boolean
        Dim this As T = Nothing
        this = alloc(Of T)()
        Return isComparable(Of T, CompareTo)(this)
    End Function

    Public Const object_compare_undetermined As SByte = 2

    Public Function object_compare(ByVal this As Object, ByVal that As Object) As SByte
        If Object.ReferenceEquals(this, that) Then
            Return 0
        ElseIf this Is Nothing Then
            Return -1
        ElseIf that Is Nothing Then
            Return 1
        Else
            Return object_compare_undetermined
        End If
    End Function

    Private Sub compare_error(Of T, T2)(ByVal ex As Exception)
        raiseError("caught exception when comparing " + _
                   GetType(T).FullName() + " with " + GetType(T2).FullName() + _
                   ", ex " + ex.Message + ", callstack " + ex.StackTrace(), errorHandle.errorType.exclamation, _
                   additionaljump:=1)
    End Sub

    Private Function compareImpl(Of T, T2)(ByVal compareTo As _do(Of Int32)) As Int32
        Try
            Return compareTo()
        Catch ex As Exception
            If Not istype(Of Threading.ThreadAbortException)(ex) Then
                compare_error(Of T, T2)(ex)
            End If
            Return npos
        End Try
    End Function

    Private Function compareImpl(Of T, T2)(ByVal this As IComparable, ByVal that As T2) As Int32
        Return compareImpl(Of T, T2)(Function() As Int32
                                         Return this.CompareTo(that)
                                     End Function)
    End Function

    Private Function compareImpl(Of T, T2)(ByVal this As IComparable(Of T2), ByVal that As T2) As Int32
        Return compareImpl(Of T, T2)(Function() As Int32
                                         Return this.CompareTo(that)
                                     End Function)
    End Function

    Public Function compare(Of T, T2)(ByVal this As T, ByVal that As T2) As Int32
        Dim cmp As SByte
        cmp = object_compare(this, that)
        If cmp = object_compare_undetermined Then
            If istype(Of IComparable(Of T2))(this) Then
                Return compareImpl(Of T, T2)(convertor.cast(Of IComparable(Of T2))(this), that)
            ElseIf istype(Of IComparable(Of T))(that) Then
                Return -compareImpl(Of T2, T)(convertor.cast(Of IComparable(Of T))(that), this)
            ElseIf istype(Of IComparable)(this) Then
                Return compareImpl(Of T, T2)(convertor.cast(Of IComparable)(this), that)
            ElseIf istype(Of IComparable)(that) Then
                Return -compareImpl(Of T2, T)(convertor.cast(Of IComparable)(that), this)
            Else
                raiseError("caught instance do not have IComparable implement or is nothing, unable to compare. " + _
                           GetType(T).FullName, errorHandle.errorType.exclamation)
                Return npos
            End If
        Else
            Return cmp
        End If
    End Function

    Public Function cast_compare(Of T, T2, CompT)(ByVal this As T, ByVal that As T2) As Int32
        Return compare(this, convertor.cast(Of CompT)(that))
    End Function

    Public Function compare(Of T, T2 As CompT, CompT)(ByVal this As T, ByVal that As T2) As Int32
        Return compare(this, convertor.cast(Of CompT)(that))
    End Function

    Public Function compare(Of T)(ByVal this As T, ByVal that As Object) As Int32
        Return compare(Of T, Object)(this, that)
    End Function

    Public Function equals(Of T, T2)(ByVal this As T, ByVal that As T2) As Boolean
        Return compare(this, that) = 0
    End Function

    Public Function equals(Of T)(ByVal this As T, ByVal that As T) As Boolean
        Return compare(Of T, T)(this, that) = 0
    End Function

    Public Function equals(Of T)(ByVal this As T, ByVal that As Object) As Boolean
        Return compare(Of T)(this, that) = 0
    End Function

    Private Delegate Function compareDelegate(Of T, T2)(ByVal this As T, ByVal that As T2) As Int32

    Private Function operatorEqualImpl(Of T, T2)(ByVal this As T, ByVal that As T2, _
                                                 ByVal compare As compareDelegate(Of T, T2)) As Boolean
        assert(Not compare Is Nothing, "compare is a nothing compareDelegate.")
        If this Is Nothing AndAlso that Is Nothing Then
            Return True
        ElseIf this Is Nothing OrElse that Is Nothing Then
            Return False
        Else
            Return compare(this, that) = 0
        End If
    End Function

    'save a call to compare(Of T,T2), and IComparable(Of Object)
    Public Function operatorEqual(Of T)(ByVal this As T, ByVal that As Object) As Boolean
        Return operatorEqualImpl(this, that, AddressOf compare(Of T))
    End Function

    Public Function operatorUnequal(Of T)(ByVal this As T, ByVal that As Object) As Boolean
        Return Not operatorEqual(this, that)
    End Function

    Public Function operatorEqual(Of T, T2)(ByVal this As T, ByVal that As T2) As Boolean
        Return operatorEqualImpl(this, that, AddressOf compare(Of T, T2))
    End Function

    Public Function operatorUnequal(Of T, T2)(ByVal this As T, ByVal that As T2) As Boolean
        Return Not operatorEqual(this, that)
    End Function

    Private Function operatorLessImpl(Of T, T2)(ByVal this As T, ByVal that As T2, _
                                                ByVal compare As compareDelegate(Of T, T2)) As Boolean
        assert(Not compare Is Nothing)
        If this Is Nothing AndAlso that Is Nothing Then
            Return False
        ElseIf this Is Nothing Then
            Return True
        ElseIf that Is Nothing Then
            Return False
        Else
            Return compare(this, that) < 0
        End If
    End Function

    Public Function operatorLess(Of T)(ByVal this As T, ByVal that As Object) As Boolean
        Return operatorLessImpl(this, that, AddressOf compare(Of T))
    End Function

    Public Function operatorMore(Of T)(ByVal this As T, ByVal that As Object) As Boolean
        Return Not operatorLess(this, that)
    End Function

    Public Function operatorLess(Of T, T2)(ByVal this As T, ByVal that As T2) As Boolean
        Return operatorLessImpl(this, that, AddressOf compare(Of T, T2))
    End Function

    Public Function operatorMore(Of T, T2)(ByVal this As T, ByVal that As T2) As Boolean
        Return Not operatorLess(this, that)
    End Function
End Module
